package com.tiger.weixin.token;

import com.tiger.common.core.redis.RedisCache;
import com.tiger.weixin.config.WxProperties;
import com.tiger.weixin.dto.AccessToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * @Author: spzhang
 * @Date: Created in 13:52 2023/2/20
 * @Description: access toekn 访问上下文
 * @Version:
 */
@Slf4j
@Component
public class AccessTokenContext {
    private String grantType;
    private String appid;
    private String secret;
    private String url;

    private static final String ACCESS_TOKEN_KEY = "wx_access_token_key:";

    @Autowired
    private RestTemplate weixinRestTemplate;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private RedisLockRegistry redisLockRegistry;

    public AccessTokenContext(WxProperties wxProperties) {
        this.grantType = wxProperties.getAccessToken().getGrantType();
        this.appid = wxProperties.getAccessToken().getAppid();
        this.secret = wxProperties.getAccessToken().getSecret();
        this.url = wxProperties.getAccessToken().getUrl();
    }

    public AccessToken getAccessTokenSerial() {
        Lock lock = redisLockRegistry.obtain("ACCESS_TOKEN_KEY");
        try {
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
                return getAccessToken();
            }
        } catch (InterruptedException e) {
            log.error("obtain lock error", e);
        } finally {
            lock.unlock();
        }
        return null;
    }

    private AccessToken getAccessToken() {
        Object cacheObject = redisCache.getCacheObject(ACCESS_TOKEN_KEY);
        if (cacheObject == null) {
            AccessToken newAccessToken = getNewAccessToken();
            redisCache.setCacheObject(ACCESS_TOKEN_KEY, newAccessToken, 5400, TimeUnit.SECONDS);
            return newAccessToken;
        }
        return (AccessToken) cacheObject;
    }

    private AccessToken getNewAccessToken() {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("grant_type", grantType);
        params.add("appid", appid);
        params.add("secret", secret);

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url).queryParams(params);
        HttpEntity<String> httpEntity = new HttpEntity<>(null, httpHeaders);
        ResponseEntity<HashMap> responseEntity = weixinRestTemplate.exchange(builder.toUriString(), HttpMethod.GET, httpEntity, HashMap.class);
        HttpStatus statusCode = responseEntity.getStatusCode();

        if (statusCode.is2xxSuccessful()) {
            HashMap<String, Object> body = responseEntity.getBody();

            Object errcode = body.get("errcode");
            if (errcode != null) {
                log.error("获取accessToken失败，errcode={}，errmsg={}", errcode, body.get("errmsg"));
                return null;
            }

            AccessToken accessToken = new AccessToken();
            Object access_token = body.get("access_token");
            Object expires_in = body.get("expires_in");
            if (access_token == null) {
                log.error("获取accessToken失败，accessToken为空");
                return null;
            }
            accessToken.setAccessToken((String) access_token);
            if (expires_in != null) {
                accessToken.setExpiresIn((Integer) expires_in);
            }

            return accessToken;
        }
        log.error("获取accessToken失败！url:{}, params:{}, headers:{}, responseEntity:{}",
                url, params, httpHeaders, responseEntity);
        return null;
    }
}
